package com.ganbing518.commons.aspect;

import com.alibaba.fastjson.JSON;
import com.ganbing518.commons.annotation.BizLogger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Description:
 *
 * @author gan bing
 * @version V1.0
 * @date 2018-06-22 15:13
 */
@Aspect
@Component
public class BizLoggerAspect implements Ordered {

    private final static Logger LOGGER = LoggerFactory.getLogger(BizLoggerAspect.class);
    private final static String COMMON_TRACE_KEY = "commons-trace-id";

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }

    @Around("@annotation(com.ganbing518.commons.annotation.BizLogger)")
    public Object bizLogAround(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;

        Method method = methodSignature.getMethod();
        Class target = point.getTarget().getClass();
        Object[] args = point.getArgs();

        if (args == null) {
            args = new Object[0];
        }

        BizLogger bizLogger = AnnotationUtils.findAnnotation(method, BizLogger.class);
        StopWatch stopWatch = null;
        if (bizLogger.watchTime()) {
            stopWatch = new StopWatch();
            stopWatch.start();
        }

        StringBuilder sb = new StringBuilder(bizLogger.desc());
        sb.append(", class:").append(target.getSimpleName());
        sb.append(", method:").append(method.getName());
        if (bizLogger.isLogParam()) {
            sb.append(", params:").append(JSON.toJSONString(args));
        }
        Object obj = null;
        boolean throwException = false;
        Throwable throwable = null;
        try {
            obj = point.proceed();
        } catch (Throwable e) {
            if (!bizLogger.isCatchException()) {
                throwException = true;
                throwable = e;
                throw e;
            }
        } finally {
            if (bizLogger.isLogResult()) {
                if (throwException) {
                    sb.append(", result:").append("throw Exception");
                } else {
                    sb.append(", result:");
                    if (obj != null) {
                        sb.append(JSON.toJSONString(obj));
                    } else {
                        sb.append("null");
                    }
                }

            }
            if (stopWatch != null && stopWatch.isRunning()) {
                stopWatch.stop();
                sb.append(", elapsed:").append(stopWatch.getTotalTimeMillis()).append("ms");
            }
            if (throwException) {
                LOGGER.info(sb.toString(), throwable);
            } else {
                LOGGER.info(sb.toString());
            }
        }
        return obj;
    }

}
